using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Text;

namespace Universe.JRun
{
    public class JCompiler : IDisposable
    {
        private string _Body;
        private string _classPath;
        private JDescription _JDK;

        private bool _isCompiled;
        object _syncCompile = new object();
        private string _folder;
        private string _className = "Class_52440DE2E3D242ba8ABD9C838D3DC974";
        private string _errors;
        private string _output;


        public JCompiler()
        {
        }


        public JCompiler(string body)
        {
            _Body = body;
        }


        public JCompiler(string body, string classPath)
        {
            _Body = body;
            _classPath = classPath;
        }


        public string ClassPath
        {
            get { return _classPath; }
            set { _classPath = value; }
        }

        public string Body
        {
            get { return _Body; }
            set { _Body = value; }
        }


        public JDescription JDK
        {
            get { return _JDK; }
            set { _JDK = value; }
        }

        private JDescription GetJDK()
        {
            JDescription j = JDK;
            if (j == null || !j.Exists)
                j = JDescriptions.JDK.Current;

            return j;
        }


        public JRunner Compile()
        {
            JDescription jdk = null;

            if (!_isCompiled)
                lock (_syncCompile)
                    if (!_isCompiled)
                    {
                        _folder = Path.Combine(
                            Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData),
                            "Temp\\Universe.JRun\\" + Guid.NewGuid()
                            );

                        string source =
                            "public class " + _className + " { public static void main(String[] arguments) {"
                            + Environment.NewLine
                            + Body
                            + Environment.NewLine
                            + "} }"
                            + Environment.NewLine;

                        Directory.CreateDirectory(_folder);
                        using (
                            FileStream fs =
                                new FileStream(Path.Combine(_folder, _className + ".java"), FileMode.Create, FileAccess.Write,
                                               FileShare.Read))
                        {
                            byte[] bytes = Encoding.UTF8.GetBytes(source);
                            fs.Write(bytes, 0, bytes.Length);
                        }

                        jdk = GetJDK();
                        ProcessStartInfo si = new ProcessStartInfo(
                            Path.Combine(jdk.Path, "bin\\javac.exe"),
                            "-g -encoding utf-8 "
                            + (string.IsNullOrEmpty(ClassPath) ? "" : ("-cp \"" + ClassPath + "\" "))
                            + ("\"" + _folder + "\\" + _className + ".java" + "\""));

                        // Trace.WriteLine("\"" + si.FileName + "\"" + " " + si.Arguments);
                        using(FileStream fs = new FileStream(Path.Combine(_folder, "compile.cmd"), FileMode.Create, FileAccess.Write, FileShare.Read))
                        using(StreamWriter wr = new StreamWriter(fs, new UTF8Encoding(false)))
                        {
                            wr.WriteLine("\"" + si.FileName + "\"" + " " + si.Arguments);
                        }

                        si.UseShellExecute = false;
                        si.CreateNoWindow = true;
                        si.ErrorDialog = false;
                        si.RedirectStandardError = true;
                        si.RedirectStandardOutput = true;
                        si.StandardErrorEncoding = Encoding.UTF8;
                        si.StandardOutputEncoding = Encoding.UTF8;
                        using (Process p = Process.Start(si))
                        {
                            p.WaitForExit();
                            _errors = p.StandardError.ReadToEnd();
                            _output = p.StandardOutput.ReadToEnd();

                            if (!string.IsNullOrEmpty(_errors))
                            {
                                Trace.WriteLine(_output);
                                Trace.WriteLine(_errors);
                            }

                        }

                        _isCompiled = true;
                    }

            if (!string.IsNullOrEmpty(_errors))
                throw new JCompilerException("Failed to compile source. " + Environment.NewLine + _errors);

            string rcp =
                (string.IsNullOrEmpty(ClassPath) ? "" : (ClassPath + ";")) + _folder;


            JRunner ret = new JRunner(_className, rcp);
            ret.JRE = jdk;
            return ret;
        }


        public void Dispose()
        {
            if (_isCompiled)
                try
                {
#if DEBUG
                    FileSystemKiller.DeleteFolderOnReboot(_folder);
#else
                    FileSystemKiller.DeleteFolder(_folder);
#endif
                }
                catch (Exception ex)
                {
                    Trace.WriteLine("Failed to dispose. " + Environment.NewLine + ex);
                }

            GC.SuppressFinalize(this);
        }

        ~JCompiler()
        {
            Dispose();
        }
    }
}

